﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace d39
{
    public partial class GridPlayers : GridPage
    {
        public override string SubTitle
        {
            get { return "Players"; }
        }

        private class AddPlayerCommand : GridCommand
        {
            private Player player;
            internal AddPlayerCommand(GridPlayers page, Player player)
                : base(page)
            {
                this.player = player;
            }
            public override string Text
            {
	            get { return "Place " + player.Name + " at {}"; }
            }
            public override void  Invoke(HalfCoordinate coord)
            {
                player.Positions.Add(new PositionMove(player, (Coordinate)coord, new List<Vector>()));
                GridPage.GridChanged(true, false);
            }
        }
        private class RemoveCommand : GridCommand
        {
            private Player player;
            private int? positionIndex;

            internal RemoveCommand(GridPlayers page, Player player, int? positionIndex)
                : base(page)
            {
                this.player = player;
                this.positionIndex = positionIndex;
            }

            public override string Text
            {
                get
                {
                    var pos = "{}";
                    if (positionIndex != null) pos = player.Positions[(int)positionIndex].Position.ToHumanReadableString();
                    return "Remove " + player.Name + " from " + pos;
                }
            }

            public override IEnumerable<HalfCoordinate> Cells
            {
                get
                {
                    if (positionIndex == null)
                    {
                        return from p in player.GetRevealedPositions(CurrentGrid) select (HalfCoordinate)p.Position;
                    }
                    else
                    {
                        return from x in Enumerable.Range(0, player.SizeInSquares)
                               from y in Enumerable.Range(0, player.SizeInSquares)
                               select (HalfCoordinate)player.Positions[(int)positionIndex].Position + Vector.E * x + Vector.S * y;
                    }
                }
            }

            public override void Invoke(HalfCoordinate coord)
            {
                var index = positionIndex ?? player.Positions.FindIndex(p => p.Position == coord);
                player.Positions.RemoveAt(index);
                GridPage.GridChanged(true, false);
            }
        }
        private class SetActiveCommand : GridCommand
        {
            private Player player;
            private bool active;
            private int? positionIndex;

            internal SetActiveCommand(GridPlayers page, Player player, bool active, int? positionIndex)
                : base(page)
            {
                this.player = player;
                this.active = active;
                this.positionIndex = positionIndex;
            }

            public override string Text
            {
                get
                {
                    var pos = "{}";
                    if (positionIndex != null) pos = player.Positions[(int)positionIndex].Position.ToHumanReadableString();
                    return "Set " + player.Name + (player.GetRevealedPositions(CurrentGrid).Count() > 1 ? " at " + pos : "") + (active ? " active" : " inactive (dead/disabled)");
                }
            }

            public override IEnumerable<HalfCoordinate> Cells
            {
                get
                {
                    if (positionIndex == null)
                    {
                        return from p in player.GetRevealedPositions(CurrentGrid) where p.IsInactive == active select (HalfCoordinate)p.Position;
                    }
                    else
                    {
                        return from x in Enumerable.Range(0, player.SizeInSquares)
                               from y in Enumerable.Range(0, player.SizeInSquares)
                               select (HalfCoordinate)player.Positions[(int)positionIndex].Position + Vector.E * x + Vector.S * y;
                    }
                }
            }

            public override void Invoke(HalfCoordinate coord)
            {
                var index = positionIndex ?? player.Positions.FindIndex(p => p.Position == coord);
                player.Positions[index].IsInactive = !active;
                GridPage.GridChanged(true, false);
            }
        }
        private class SetInvisibleCommand : GridCommand
        {
            private Player player;
            private bool invisible;
            private int? positionIndex;

            internal SetInvisibleCommand(GridPlayers page, Player player, bool invisible, int? positionIndex)
                : base(page)
            {
                this.player = player;
                this.invisible = invisible;
                this.positionIndex = positionIndex;
            }

            public override string Text
            {
                get
                {
                    var pos = "{}";
                    if (positionIndex != null) pos = player.Positions[(int)positionIndex].Position.ToHumanReadableString();
                    return "Set " + player.Name + (player.GetRevealedPositions(CurrentGrid).Count() > 1 ? " at " + pos : "") + (invisible ? " invisible" : " visible");
                }
            }

            public override IEnumerable<HalfCoordinate> Cells
            {
                get
                {
                    if (positionIndex == null)
                    {
                        return from p in player.GetRevealedPositions(CurrentGrid) where p.IsInvisible != invisible select (HalfCoordinate)p.Position;
                    }
                    else
                    {
                        return from x in Enumerable.Range(0, player.SizeInSquares)
                               from y in Enumerable.Range(0, player.SizeInSquares)
                               select (HalfCoordinate)player.Positions[(int)positionIndex].Position + Vector.E * x + Vector.S * y;
                    }
                }
            }

            public override void Invoke(HalfCoordinate coord)
            {
                var index = positionIndex ?? player.Positions.FindIndex(p => p.Position == coord);
                player.Positions[index].IsInvisible = invisible;
                GridPage.GridChanged(true, false);
            }
        }

        private class SetUnknownCommand : GridCommand
        {
            private Player player;
            private bool unknown;
            private int? positionIndex;

            internal SetUnknownCommand(GridPlayers page, Player player, bool unknown, int? positionIndex)
                : base(page)
            {
                this.player = player;
                this.unknown = unknown;
                this.positionIndex = positionIndex;
            }

            public override string Text
            {
                get
                {
                    var pos = "{}";
                    if (positionIndex != null) pos = player.Positions[(int)positionIndex].Position.ToHumanReadableString();
                    return (unknown ? "Hide " : "Reveal ") + player.Name + (player.GetRevealedPositions(CurrentGrid).Count() > 1 ? " at " + pos : "");
                }
            }

            public override IEnumerable<HalfCoordinate> Cells
            {
                get
                {
                    if (positionIndex == null)
                    {
                        return from p in player.GetRevealedPositions(CurrentGrid) where p.IsUnknown != unknown select (HalfCoordinate)p.Position;
                    }
                    else
                    {
                        return from x in Enumerable.Range(0, player.SizeInSquares)
                               from y in Enumerable.Range(0, player.SizeInSquares)
                               select (HalfCoordinate)player.Positions[(int)positionIndex].Position + Vector.E * x + Vector.S * y;
                    }
                }
            }

            public override void Invoke(HalfCoordinate coord)
            {
                var index = positionIndex ?? player.Positions.FindIndex(p => p.Position == coord);
                player.Positions[index].IsUnknown = unknown;
                GridPage.GridChanged(true, false);
            }
        }

        public override IEnumerable<GridCommand> Commands
        {
            get
            {
                if (Players.EditIndex >= 0) yield break;

                foreach (var command in
                        from player in CurrentGrid.RevealedPlayers
                        where player.Size > 1
                        from pos in player.GetRevealedPositions(CurrentGrid)
                        select new RemoveCommand(this, player, player.Positions.IndexOf(pos)))
                {
                    yield return command;
                }
                foreach (var command in
                        from player in CurrentGrid.RevealedPlayers
                        where player.Size <= 1 && player.GetRevealedPositions(CurrentGrid).Any()
                        select new RemoveCommand(this, player, null))
                {
                    yield return command;
                }
                foreach (var command in
                        from player in CurrentGrid.RevealedPlayers
                        where player.Size > 1
                        from pos in player.GetRevealedPositions(CurrentGrid)
                        select new SetInvisibleCommand(this, player, !pos.IsInvisible, player.Positions.IndexOf(pos)))
                {
                    yield return command;
                }
                foreach (var p in
                        from player in CurrentGrid.RevealedPlayers
                        where player.Size <= 1 && player.GetRevealedPositions(CurrentGrid).Any()
                        select player)
                {
                    if (p.GetRevealedPositions(CurrentGrid).Any(pm => !pm.IsInvisible)) yield return new SetInvisibleCommand(this, p, true, null);
                    if (p.GetRevealedPositions(CurrentGrid).Any(pm => pm.IsInvisible)) yield return new SetInvisibleCommand(this, p, false, null);
                }
                foreach (var command in
                        from player in CurrentGrid.RevealedPlayers
                        where player.Size > 1
                        from pos in player.GetRevealedPositions(CurrentGrid)
                        select new SetUnknownCommand(this, player, !pos.IsUnknown, player.Positions.IndexOf(pos)))
                {
                    yield return command;
                }
                foreach (var p in
                        from player in CurrentGrid.RevealedPlayers
                        where player.Size <= 1 && player.GetRevealedPositions(CurrentGrid).Any()
                        select player)
                {
                    if (p.GetRevealedPositions(CurrentGrid).Any(pm => !pm.IsUnknown)) yield return new SetUnknownCommand(this, p, true, null);
                    if (p.GetRevealedPositions(CurrentGrid).Any(pm => pm.IsUnknown)) yield return new SetUnknownCommand(this, p, false, null);
                }
                foreach (var command in
                        from player in CurrentGrid.RevealedPlayers
                        where player.Size > 1
                        from pos in player.GetRevealedPositions(CurrentGrid)
                        select new SetActiveCommand(this, player, pos.IsInactive, player.Positions.IndexOf(pos)))
                {
                    yield return command;
                }
                foreach (var p in
                        from player in CurrentGrid.RevealedPlayers
                        where player.Size <= 1 && player.GetRevealedPositions(CurrentGrid).Any()
                        select player)
                {
                    if (p.GetRevealedPositions(CurrentGrid).Any(pm => pm.IsInactive)) yield return new SetActiveCommand(this, p, true, null);
                    if (p.GetRevealedPositions(CurrentGrid).Any(pm => !pm.IsInactive)) yield return new SetActiveCommand(this, p, false, null);
                }
                foreach (var player in CurrentGrid.RevealedPlayers.Where(p => p.IsMulti || !p.GetRevealedPositions(CurrentGrid).Any())) yield return new AddPlayerCommand(this, player);
            }
        }
        
        protected void Page_Load(object sender, EventArgs e)
        {
            Players.SetEditableListDataSource(CurrentGrid.Players, AddPlayerBtn,
                () => GridChanged(true, false), // Forces the grid commands to rebind based on whether IsEditing is true or not
                () => GridChanged(true, false));
        }

        protected void RollInitiative_Click(object sender, EventArgs e)
        {
            CurrentGrid.Players.RollInitiative();
            GridChanged(true, false);
        }
        protected void RevealUnknown_CheckedChanged(object sender, EventArgs e)
        {
            CurrentGrid.RevealUnknown = RevealUnknown.Checked;
            GridChanged(true, false);
        }

        protected bool InitiativeApplicable { get { return CurrentGrid.Players.InitiativeApplicable(); } }
        protected bool UnknownApplicable { get { return CurrentGrid.Players.Any(p => p.Positions.Any(pos => pos.IsUnknown)); } }
        
        protected string GetPositionsString(object playerObj)
        {
            var player = (Player)playerObj;
            var positions = player.GetRevealedPositions(CurrentGrid);
            return positions.Any() ? ": " + player.GetRevealedPositionsString(CurrentGrid) : null;
        }

        protected override void UpdateFromGrid(bool playersOrMovesChanged, bool wallTerrainChanged)
        {
            if (playersOrMovesChanged)
            {
                UpdatePanel.Update();
                DataBind();
            }
        }
    }
}